<?php
/**
 * Game_Unit_Status
 *
 * Класс Game_Unit_Status отвечает за обработку статуса юнита
 *
 * @author Дмитрий Кузьма <kouzma.d@gmail.com>
 * @version 1.0
 */
class Game_Unit_Status {
	
	private $_unitId; // ID юнита
	private $_isResearched; // флаг, исследован юнит или нет
	private $_level; // уровень исследования юнита
	private $_isResearchingNow; // флаг, исследуется юнит в данный момент, или нет
	private $_objResearchCost; // массив с информацией о стоимостях исследований
	private $_arrBuildingsUnperformedDependences; // массив невыполненных зависимостей от зданий
	
	
	private static $_arrStatusesObjects; // Массив объектов с информацией о статусах в месте
	private static $_arrGameBuildingsDependecesByKeys; // Все возможные в игре зависимотси юнитов от зданий
	
	/**
	 * Создает новый объект статуса
	 * 
	 * @param int $unitId
	 * @param bool $isResearched
	 * @param Game_Unit_ResearchCost $objResearchCost - информация о том, сколько стоит исследование
	 * @param bool $isResearchingNow
	 * @param int $level - уровень апгрейда юнита
	 */
	private function __construct($unitId, $isResearched, Game_Unit_ResearchCost $objResearchCost, $isResearchingNow, $level) {
		$this->_unitId = $unitId;
		$this->_isResearched = $isResearched;
		$this->_level = $level;
		$this->_isResearchingNow = $isResearchingNow;
		$this->_objResearchCost = $objResearchCost;
	}
	
	/**
	 * Возвращает флаг, исследован ли данный юнит
	 */
	public function isResearched() {
		return $this->_isResearched;
	}
	
	/**
	 * Возвращает уровень апргрейда юнита
	 */
	public function getLevel() {
		return $this->_level;
	}
	
	/**
	 * Возвращает флаг, исследуется ли юнит в данный момент
	 */
	public function isResearchingNow() {
		return $this->_isResearchingNow;
	}
	
	/**
	 * Возвращает идентификатор юнита
	 */
	public function getUnitId() {
		return $this->_unitId;
	}
	
	/**
	 * Возвращает идентификатор юнита
	 */
	public function getResearchCost() {
		return $this->_objResearchCost;
	}
	
	/**
	 * Возвращает идентификатор юнита
	 */
	public function getBuildingsUnperformedDependences() {
		return $this->_arrBuildingsUnperformedDependences;
	}
	
	/**
	 * Определяет время исследования юнита с учетом уровня исследовательского здания
	 *
	 * @return int
	 */
	public function getResearchTime() {
		return $this->_objResearchCost->getTimeBase() / Game_Building_Academy::getResearchSpeed();
	}
	
	/**
	 * Определяет, какие юниты, какой статус исследования/зависимостей от зданий имеют
	 */
	public static function getPlaceUnitsStatuses() {
		if (!empty(self::$_arrStatusesObjects)) return self::$_arrStatusesObjects;
		$objDb = Service_Database::instance();
		$objGeneral = Game_General::instance();
		$placeId = $objGeneral->objPlace->id;
		$arrResearchEvents = $objGeneral->objPlace->getResearchsEvents();
		//$arrAllUnits = self::getAllUnitsInfo();
		
	
		// Получаем объекты исследования всех юнитов
		$stmt = "
			SELECT *
			FROM `unit_research`
			ORDER BY `ID_unit` ASC
		";
		$arrUnits = $objDb->select($stmt);
		$arrUnitsObjects = array();
		foreach ($arrUnits as $arrUnit) {
			$arrUnitsObjects[$arrUnit['ID_unit']] = new Game_Unit_ResearchCost($arrUnit);
		}
		// Определяем, что исследовано
		$stmt = "
			SELECT *
			FROM `place_research`
			WHERE `ID_place` = '" . $placeId . "'
		";
		$arrResearchedByKeys = Service_ArrayConverter::makeKeyArrayFromMultyArray($objDb->select($stmt), 'ID_unit');
		
		
		// Создаем объекты с информацией о статусах
		self::$_arrStatusesObjects = array();
		foreach ($arrUnitsObjects as $unitId => $objResearchCost) {
			$isResearched = false;
			if ($objResearchCost->getField('is_autoresearched')) $isResearched = true;
			elseif (isset($arrResearchedByKeys[$unitId]['is_researched'])) $isResearched = true;
			$level = 0;
			if (isset($arrResearchedByKeys[$unitId]['level'])) $level = $arrResearchedByKeys[$unitId]['level'];
			if ($unitId > 3) continue;
			self::$_arrStatusesObjects[$unitId] =	new Game_Unit_Status($unitId, $isResearched, $objResearchCost, !empty($arrResearchEvents[$unitId]), $level);
		}
		

		foreach (self::$_arrStatusesObjects as &$objUnitStatus) {
			$objUnitStatus->_fillDependences();
		}
		return self::$_arrStatusesObjects;
	}
	
	/**
	 * Заполняет зависимости от зданий
	 */
	private function _fillDependences() {
		$this->_arrBuildingsUnperformedDependences = array();
		$objGeneral = Game_General::instance();
		$arrPlaceBuildings = $objGeneral->objPlace->getBuildingsData();
		
		$arrStartDependences = $this->_getStartDependences();
		foreach ($arrStartDependences as $arrDependency) {
			$isNeed = true;
			foreach ($arrPlaceBuildings as $arrBuilding) {
				// Если построено необходимое здание
				if ($arrBuilding['ID_building'] == $arrDependency['ID_building_need']) {
					// Если уровень здания достаточен
					if ($arrBuilding['level'] >= $arrDependency['level']) {
						$isNeed = false;
						break;
					}
				}
			}
			if ($isNeed) $this->_arrBuildingsUnperformedDependences[] = $arrDependency;
		}
	}
	
	/**
	 * Возвращает все зависимости юнитов
	 *
	 * @return array:
	 */
	private static function _getUnitsDependences() {
		if (!isset(self::$_arrGameBuildingsDependecesByKeys)) {
			$objDb = Service_Database::instance();
			$stmt = "
				SELECT *
				FROM `unit_dependence`
			";
			$arrDependences = $objDb->select($stmt);
			 
			foreach ($arrDependences as $arrDependency) {
				$unitId = $arrDependency['ID_unit'];
				if (empty(self::$_arrGameBuildingsDependecesByKeys[$unitId])) self::$_arrGameBuildingsDependecesByKeys[$unitId] = array();
				self::$_arrGameBuildingsDependecesByKeys[$unitId][] = $arrDependency;
			}
			 
		}
		return self::$_arrGameBuildingsDependecesByKeys;
	}
	
	/**
	 * Получает список зависимостей юнита
	 * 
	 * @return array
	 */
	private function _getStartDependences() {
		self::_getUnitsDependences();
		if (empty(self::$_arrGameBuildingsDependecesByKeys[$this->getUnitId()])) return array();
		return self::$_arrGameBuildingsDependecesByKeys[$this->getUnitId()];
	}
	
	
	/**
	 * Возвращает статуст исследования конкретного юнита
	 * 
	 * @param int $unitId
	 * @return Game_Unit_Status:
	 */
	public static function getPlaceUnitStatus($unitId) {
		self::getPlaceUnitsStatuses();
		if (empty(self::$_arrStatusesObjects[$unitId])) return false;
		return self::$_arrStatusesObjects[$unitId];
	}
	
	/**
	 * Метод осуществляет исследование юнита
	 *
	 * @throws Exception_Game 
	 */
	public function research() {
		// Проверяем, исследован ли он уже
		if ($this->isResearched()) throw new Exception_Game_Exist(Service_Translate::getText('Юнит уже исследован'));
		
		// Проверяем, не исследуется ли в данный момент
		if (Game_Event_Research::isPlaceEventExist($this->_unitId)) throw new Exception_Game_CurrentAction(Service_Translate::getText('Юнит в данный момент исследуется'));
		
		$objGeneral = Game_General::instance();
		// Проверяем, не исследуется ли в данный момент что-то другое в месте
		if ($objGeneral->objPlace->hasResearchEvents()) throw new Exception_Game_BusyWorkers(Service_Translate::getText('В данный момент проходят другие исследования'));
		
		// Проверяем, готовы ли все зависимости для иследования
		$arrBuildingsUnperformedDependences =  $this->getBuildingsUnperformedDependences();
		if (!empty($arrBuildingsUnperformedDependences)) throw new Exception_Game_BuildingsDependences();
		
		// Проверяем, достаточно ли ресурсов
		
		if (!$objGeneral->objPlace->objResourcesWithProduction->isEnoughResources($this->getResearchCost()->objResources)) {
			throw new Exception_Game_Resources();
		}
		
		return $this->_makeResearch();
	}
	
	/**
	 * Метод оплачивает исследование юнита и создает соответствующее игровое событие
	 * 
	 * @return boolean
	 */
	private function _makeResearch() {
		// TODO по хорошему можно SQL транзакцию вставить, но не обязательно
		$objResourcesCost = $this->_objResearchCost->objResources;
		$objGeneral = Game_General::instance();
		$objGeneral->pay($objResourcesCost);
	
		$this->_createResearchEvent();
		return true;
	}
	
	/**
	 * Создает событие "Исследование юнита"
	 *
	 */
	private function _createResearchEvent() {
		$objGeneral = Game_General::instance();
		$objGameEvent = new Game_Event(array(
				'ID_place' => $objGeneral->objPlace->id,
				'ID_user' => $objGeneral->objUser->id,
				'event_type' => 'research',
				'ID_essence' => $this->_unitId,
				'amount' => 1,
				'event_time' => Service_Date::getTimestampToDBSaveWithSecondsMove($this->getResearchTime())
		));
		$objGameEvent->save();
	}
}
?>